﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Timers;
using System.Threading;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;

namespace server
{
    public partial class chat : Form
    {
        private const int DIM = 1024;
        private const int N_CLIENT = 4;
        private int act_client = 0;
        private bool message;
        private UInt16 VideoPort, ClipPort;
        private Thread accettaClient;
        private Thread[] inRicezione = new Thread[N_CLIENT];
        private TcpListener l, listClip;
        private TcpClient[] sClient = new TcpClient[N_CLIENT];

        private String User, password;
        private string[] client = new string[N_CLIENT];
        private Stream[] str = new Stream[N_CLIENT];
        private bool[] free = new bool[N_CLIENT];
        private Object ogg = new Object();

        /* Clipboard */
        [DllImport("User32.dll")]
        protected static extern int SetClipboardViewer(int hWndNewViewer);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

        private TcpClient[] sClientClip = new TcpClient[N_CLIENT];
        private Stream[] strClip = new Stream[N_CLIENT];
        private Thread[] inClip = new Thread[N_CLIENT];
        private Object oggClip = new Object();
        private int bufLen, cont = 0;
        private IntPtr nextClipboardViewer;
        private string richClip = "";
        private byte[] sendClip;
        private String clipTypeS = "";
        private bool ctrlClip;
        private Thread accettaClipboard;
        private int act_clip = 0;
        private bool[] freeClip = new bool[N_CLIENT];
        private String[] filenameIn = new String[1];
        private String[] filenameOut = new String[1];

        public delegate void settaClip(bool value);
        public settaClip setClip;

        /******************************/

        public delegate void msg(string s);
        public msg writemsg;

        // per disabilitare il tasto X del form
        private const int CP_NOCLOSE_BUTTON = 0x200;

        public void addMsg(string s)
        {
            chatBox.Text += s;
        }

        public chat()
        {
            InitializeComponent();
        }

        public chat(String user, UInt16 video_port, UInt16 clip_port, string pass, TcpListener list, TcpListener listC)
        {
            for (int i = 0; i < N_CLIENT; i++)
            {
                free[i] = true;
                freeClip[i] = true;
            }
            User = user;
            password = pass;
            VideoPort = video_port;
            ClipPort = clip_port;
            InitializeComponent();
            writemsg = new msg(addMsg);
            UtenteConnesso.Text = user;
            l = list;
            listClip = listC;
            accettaClient = new Thread(accClient);
            accettaClient.Start();
            nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
            setClip = new settaClip(settaButt);
            filenameIn[0] = "";
            filenameOut[0] = "";
            Clipboard.Clear();
            ClipButton.Enabled = false;
            accettaClipboard = new Thread(clipAccept);
            accettaClipboard.Start();
        }

        private void settaButt(bool abilt)
        {
            ClipButton.Enabled = abilt;
        }

        public int getNClient
        {
            get { return N_CLIENT; }
        }

        public Thread[] getClipThread
        {
            get { return inClip; }
        }
        public Thread[] getChatThread
        {
            get { return inRicezione; }
        }

        public Thread getAccClipThread
        {
            get { return accettaClipboard; }
        }
        public Thread getAccChatThread
        {
            get { return accettaClient; }
        }

        public TcpClient[] getClipCon
        {
            get { return sClientClip; }
        }
        public TcpClient[] getChatCon
        {
            get { return sClient;}
        }

        private void accClient()
        {
            try
            {
                while (true)
                {
                    try
                    {
                        TcpClient tmp = l.AcceptTcpClient();
                        if (act_client < N_CLIENT)
                        {
                            byte[] buf = new byte[DIM];
                            tmp.GetStream().Write(Encoding.ASCII.GetBytes("Password".ToCharArray()), 0, "Password".Length);
                            string adaf = tmp.ToString();
                            int lung = tmp.GetStream().Read(buf, 0, DIM);
                            string pw = System.Text.Encoding.ASCII.GetString(buf).Substring(0, lung);
                            if (pw.Equals(password))
                            {
                                string str2 = "OK-" + VideoPort.ToString() + "-" + ClipPort.ToString();
                                tmp.GetStream().Write(Encoding.ASCII.GetBytes(str2.ToCharArray()), 0, (str2).Length);
                                int i;
                                for (i = 0; !free[i]; i++) ;
                                sClient[i] = tmp;
                                try
                                {
                                    str[i] = (sClient[i]).GetStream();
                                }
                                catch
                                {
                                    MessageBox.Show("Connection Error!");
                                }
                                inRicezione[i] = new Thread(ClientReceive);
                                object o;
                                o = (int)i;
                                free[i] = false;
                                inRicezione[i].Start(o);
                                act_client++;
                            }
                            else
                            {
                                tmp.GetStream().Write(Encoding.ASCII.GetBytes("Wrong_password".ToCharArray()), 0, "Wrong_password".Length);
                            }
                        }
                        else
                        {
                            tmp.GetStream().Write(Encoding.ASCII.GetBytes("Impossible".ToCharArray()), 0, "Impossible".Length);
                        }
                    }
                    catch
                    {
                        ;/* chiudere la connessione*/
                    }
                }
            }
            catch 
            {
                ;
            }
        }

        private void clipAccept()
        {
            try
            {
                while (true)
                {
                    TcpClient tmpClip = listClip.AcceptTcpClient();
                    Monitor.Enter(oggClip);
                    if (act_clip < N_CLIENT)
                    {
                        int i;
                        for (i = 0; !freeClip[i]; i++) ;
                        sClientClip[i] = tmpClip;
                        try
                        {
                            strClip[i] = (sClientClip[i]).GetStream();
                        }
                        catch
                        {
                            MessageBox.Show("Connection Error!");
                        }
                        inClip[i] = new Thread(ClipReceive);
                        object o;
                        o = (int)i;
                        freeClip[i] = false;
                        act_clip++;
                        inClip[i].SetApartmentState(ApartmentState.STA);
                        inClip[i].Start(o);
                    }
                    else
                    {
                        tmpClip.GetStream().Write(Encoding.ASCII.GetBytes("Impossible".ToCharArray()), 0, "Impossible".Length);
                    }
                    Monitor.Exit(oggClip);
                }
            }
            catch
            {
                ;
            }
        }

        private void ClientReceive(object obj)
        {
            bool continua=true;
            byte[] buf = new byte[DIM];
            bool control = false;
            int n = (int)obj;
            int i = N_CLIENT;
            string s = "New user connected: ", line="";
                
            Monitor.Enter(ogg);
            try
            {
                control = true;
                Invoke(writemsg, "\r\n");
                int len = str[n].Read(buf, 0, DIM);
                line = System.Text.Encoding.ASCII.GetString(buf);
                line = line.Substring(0, len);
                s += line;
                Invoke(writemsg, s);
                string temporaneo = User;
                for (i = 0; i < N_CLIENT; i++)
                    if (i != n && !free[i])
                    {
                        temporaneo += ", ";
                        temporaneo += client[i];
                        str[i].Write(Encoding.ASCII.GetBytes(s.ToCharArray()), 0, len + "New user connected: ".Length);
                    }
                client[n] = line;
                str[n].Write(Encoding.ASCII.GetBytes(temporaneo.ToCharArray()), 0, temporaneo.Length);
            }
            catch { continua=false;}
            Monitor.Exit(ogg);
            control = false;
            try{
                while (continua)
                {
                    try
                    {
                        int lenght = sClient[n].GetStream().Read(buf, 0, DIM);
                        if (lenght != 0)
                        {
                            //prendo le risorse
                            Monitor.Enter(ogg);
                            control = true;
                            buf[lenght] = 0;
                            s = System.Text.Encoding.ASCII.GetString(buf);
                            s = s.Substring(0, lenght);
                            for (i = 0; i < N_CLIENT; i++)
                            {
                                if (i != n && !free[i])
                                {
                                    string st = "<";
                                    st += client[n];
                                    st += ">";
                                    st += s.Substring(0, lenght);
                                    str[i].Write(Encoding.ASCII.GetBytes(st.ToCharArray()), 0, st.Length);
                                }
                            }
                            Invoke(writemsg, "\r\n");
                            Invoke(writemsg, "<");
                            Invoke(writemsg, line);
                            Invoke(writemsg, ">: ");
                            Invoke(writemsg, s);
                            //rilascio le risorse
                            Monitor.Exit(ogg);
                            control = false;
                        }
                        else
                        {
                            continua = false;
                        }
                    }
                    catch
                    {
                        continua = false;   
                    }
                }
            }
            catch
            {
                ;
            }
            finally
            {
                act_client--;
                if (i != N_CLIENT)
                {
                    free[i] = true;
                    str[i] = null;
                }
                else
                {
                    free[n] = true;
                    str[n] = null;
                }
                if (control)
                    Monitor.Exit(ogg);
            }
        }
        private void ClipReceive(object obj)
        {
            byte[] bufClip;
            byte[] clipType = new byte[4];
            bool controlClip = false, esci = false;
            int n = (int)obj;
            BinaryReader stream = new BinaryReader(strClip[n]);
            int i = N_CLIENT, clipDim;
            try
            {
                while (!esci)
                {
                    try
                    {
                        int lenght = strClip[n].Read(clipType, 0, 4);
                        if (lenght != 0)
                        {
                            String cod = System.Text.Encoding.ASCII.GetString(clipType);
                            cod = cod.Substring(0, 4);
                            clipDim = stream.ReadInt32();
                            bufClip = new byte[clipDim];
                            strClip[n].Read(bufClip, 0, clipDim);
                            Monitor.Enter(oggClip);
                            controlClip = true;
                            Object o = new Object();
                            if (File.Exists(filenameIn[0]))
                            {
                                File.Delete(filenameIn[0]);
                            }
                            if (cod.Equals("Text"))
                            {
                                o = ByteArrayToObject(bufClip);
                                Clipboard.SetData(DataFormats.Text, o);
                            }
                            else
                            {
                                if (cod.Equals("Bitm"))
                                {
                                    o = ByteArrayToObject(bufClip);
                                    Clipboard.SetData(DataFormats.Bitmap, o);
                                }
                                if (cod.Equals("File"))
                                {
                                    o = ByteArrayToObject(bufClip);
                                    byte[] temp;
                                    filenameIn = (String[])o;
                                    filenameIn[0] = Path.GetFileName(filenameIn[0]);
                                    filenameIn[0].Replace("\\", "\\\\");
                                    using (FileStream fs = new FileStream(@filenameIn[0], FileMode.OpenOrCreate, FileAccess.Write))
                                    {
                                        long fileLen = stream.ReadInt64();
                                        int letti = DIM;
                                        temp = new byte[DIM];
                                        for (long k = 0; k < fileLen || letti == 0; k += letti)
                                        {
                                            letti = strClip[n].Read(temp, 0, DIM);
                                            fs.Write(temp, 0, letti);
                                        }
                                    }
                                    filenameIn[0] = Path.GetFullPath(@filenameIn[0]);
                                    filenameIn[0].Replace("\\", "\\\\");
                                    System.Collections.Specialized.StringCollection tmpColl = new System.Collections.Specialized.StringCollection();
                                    tmpColl.Add(filenameIn[0]);
                                    Clipboard.SetFileDropList(tmpColl);
                                    MessageBox.Show("New File in Clipboard!\nPress OK to skip - Ctrl+V to paste", "New File in Clipboard");
                                }
                            }
                            Invoke(setClip, false);
                            for (i = 0; i < N_CLIENT; i++)
                            {
                                if (i != n && !freeClip[i])
                                {
                                    strClip[i].Write(bufClip, 0, bufClip.Length);
                                }
                            }

                            Monitor.Exit(oggClip);
                            controlClip = false;
                        }
                        else
                        {
                            esci = true;
                        }
                    }
                    catch
                    {
                        esci = true;
                    }
                }
            }
            catch
            {
                ;
            }
            finally
            {
                act_clip--;
                if (i != N_CLIENT)
                {
                    freeClip[i] = true;
                    strClip[i] = null;
                }
                else
                {
                    freeClip[n] = true;
                    strClip[n] = null;
                }
                if (controlClip)
                    Monitor.Exit(oggClip);
                if (File.Exists(filenameIn[0]))
                {
                    try
                    {
                        File.Delete(filenameIn[0]);
                    }
                    catch { ;}
                }
            }
        }

        public object ByteArrayToObject(byte[] _ByteArray)
        {
            try
            {
                MemoryStream _MemoryStream = new MemoryStream();
                BinaryFormatter _BinaryFormatter = new BinaryFormatter();
                _MemoryStream.Write(_ByteArray, 0, _ByteArray.Length);
                _MemoryStream.Seek(0, SeekOrigin.Begin);
                return (Object)_BinaryFormatter.Deserialize(_MemoryStream);
            }
            catch
            {
                ;
            }
            return null;
        }


        public void getStringPrint(String message)
        {
            chatBox.AppendText(message + Environment.NewLine);
        }

        private void SendMessage_Click(object sender, EventArgs e)
        {
            if (!messageChat.Text.Equals(""))
            {
                try
                {
                    //prendo controllo delle risorse
                    Monitor.Enter(ogg);
                    byte[] msg = new byte[DIM];
                    if (act_client != 0)
                    {
                        string st = "<" + User + ">: " + messageChat.Text;
                        for (int i = 0; i < N_CLIENT; i++)
                            if (!free[i])
                                str[i].Write(Encoding.ASCII.GetBytes(st.ToCharArray()), 0, st.Length);
                        chatBox.AppendText(Environment.NewLine + st);
                        messageChat.Text = "";
                    }
                    else
                    {
                        chatBox.AppendText(Environment.NewLine + "Error Sending message!");
                        messageChat.Text = "";
                    }
                    //rilascio le risorse
                    Monitor.Exit(ogg);
                }
                catch
                {
                    chatBox.AppendText(Environment.NewLine + "Error Sending message!");
                    messageChat.Text = "";
                }
            }
        }

        public string txtbox
        {
            set
            {
                chatBox.Text = value + Environment.NewLine;
            }
            get
            {
                return chatBox.Text;
            }
        }

        private void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            chatBox.AppendText(messageChat.Text + Environment.NewLine);
            messageChat.Text = "";
        }

        public bool new_message
        {
            get { return message; }
            set { message = value; }
        }

        private void chat_FormClosed(object sender, FormClosedEventArgs e)
        {
            accettaClient.Abort();
        }

        public bool[] free_channel
        {
            get { return free; }
        }

        protected override void WndProc(ref Message msg)
        {
            const int WM_DRAWCLIPBOARD = 0x308;
            const int WM_CHANGECBCHAIN = 0x030D;

            switch (msg.Msg)
            {
                case WM_DRAWCLIPBOARD:
                    SendMessage(nextClipboardViewer, msg.Msg, msg.WParam, msg.LParam);
                    if (cont < 2) //if (cont < 3) //2 con client e server in remoto, 3 se entrambi in locale
                        cont++;
                    else
                        Invoke(setClip, true);
                    break;

                case WM_CHANGECBCHAIN:
                    if (msg.WParam == nextClipboardViewer)
                        nextClipboardViewer = msg.LParam;
                    else
                        SendMessage(nextClipboardViewer, msg.Msg, msg.WParam, msg.LParam);
                    break;

                default:
                    base.WndProc(ref msg);
                    break;
            }
        }

        void DisplayClipboardData()
        {
            try
            {
                Object Data = new Object();
                MemoryStream memStream = new MemoryStream();
                BinaryFormatter binFormatter = new BinaryFormatter();

                if (Clipboard.ContainsText())
                {
                    richClip = "Text- Text in Clipboard";
                    Data = Clipboard.GetData(DataFormats.Text);
                    clipTypeS = "Text";
                }
                else
                {
                    if (Clipboard.ContainsFileDropList())
                    {
                        richClip = "File- File in Clipboard";
                        Data = Clipboard.GetData(DataFormats.FileDrop);
                        filenameOut = (String[])Data;
                        clipTypeS = "File";
                    }
                    if (Clipboard.ContainsImage())
                    {
                        richClip = "Bitm- Bitmap in Clipboard";
                        Data = Clipboard.GetData(DataFormats.Bitmap);
                        clipTypeS = "Bitm";
                    }
                }
                binFormatter.Serialize(memStream, Data);
                sendClip = memStream.ToArray();
                bufLen = sendClip.Length;
            }
            catch
            {
                MessageBox.Show("ClipBoard Error!\nRetry to press clipboard button!");
            }
        }

        private void ClipboardButton_Click(object sender, EventArgs e)
        {
            DisplayClipboardData();
            if (!richClip.Equals(""))
            {
                ctrlClip = false;
                try
                {
                    Monitor.Enter(oggClip);
                    ctrlClip = true;
                    byte[] msg = new byte[DIM];
                    if (act_client != 0)
                    {
                        string st = "<" + User + ">: " + richClip;
                        for (int i = 0; i < N_CLIENT; i++)
                        {
                            if (!free[i])
                            {
                                str[i].Write(Encoding.ASCII.GetBytes(st.ToCharArray()), 0, st.Length);
                                str[i].Flush();
                            }
                            if (!freeClip[i])
                            {
                                strClip[i].Write(Encoding.ASCII.GetBytes(clipTypeS.ToCharArray()), 0, clipTypeS.Length);
                                byte[] tmp2 = BitConverter.GetBytes(bufLen);
                                strClip[i].Write(tmp2, 0, 4);
                                strClip[i].Write(sendClip, 0, bufLen);
                                strClip[i].Flush();
                                if (clipTypeS.Equals("File"))
                                {
                                    byte[] temp;
                                    filenameOut[0] = Path.GetFullPath(filenameOut[0]);
                                    filenameOut[0].Replace("\\", "\\\\");
                                    using (FileStream fs = new FileStream(filenameOut[0], FileMode.Open, FileAccess.Read))
                                    {
                                        long fileLen = fs.Length;
                                        int letti = DIM;
                                        temp = new byte[DIM];
                                        byte[] tmp64 = BitConverter.GetBytes(fileLen);
                                        strClip[i].Write(tmp64, 0, 8);
                                        for (long k = 0; k < fileLen || letti == 0; k += letti)
                                        {
                                            letti = fs.Read(temp, 0, DIM);
                                            strClip[i].Write(temp, 0, letti);
                                        }
                                    }
                                }
                            }
                        }
                        chatBox.AppendText(Environment.NewLine + st);
                    }
                    else
                    {
                        chatBox.AppendText(Environment.NewLine + "Error Sending Clipboard!");
                    }
                    Monitor.Exit(oggClip);
                    ctrlClip = false;
                }
                catch
                {
                    chatBox.AppendText(Environment.NewLine + "Error Sending Clipboard!");
                    if (ctrlClip)
                    {
                        Monitor.Exit(oggClip);
                        ctrlClip = false;
                    }
                }
                richClip = "";
                Invoke(setClip, false);
            }
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams myCp = base.CreateParams;
                myCp.ClassStyle = myCp.ClassStyle | CP_NOCLOSE_BUTTON;
                return myCp;
            }
        }
    }
}
